package com.wish233.wiliwilivideo.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.benmanes.caffeine.cache.Cache;
import com.jd.platform.hotkey.client.callback.JdHotKeyStore;
import com.wish233.wiliwiliapi.client.UserClient;
import com.wish233.wiliwiliapi.domain.dto.UserDTO;
import com.wish233.wiliwilivideo.domain.dto.comment.CreateCommentRequest;
import com.wish233.wiliwilivideo.domain.dto.comment.UpdateCommentRequest;
import com.wish233.wiliwilivideo.domain.dto.likes.LikeQueryRequest;
import com.wish233.wiliwilivideo.domain.po.Comment;
import com.wish233.wiliwilivideo.domain.vo.CommentVO;
import com.wish233.wiliwilivideo.mapper.CommentMapper;
import com.wish233.wiliwilivideo.mq.MqProducer;
import com.wish233.wiliwilivideo.service.CommentService;
import com.wish233.wiliwilivideo.service.LikesService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.wish233.wiliwilicommon.constants.RedisConstants.*;
import static com.wish233.wiliwilivideo.domain.constant.BizConstant.COMMENT_BIZ;

/**
 * @author WHH
 * @description 针对表【comment(评论表)】的数据库操作Service实现
 * @createDate 2025-03-24 16:31:06
 */
@Service
@Slf4j
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment>
        implements CommentService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private UserClient userClient;
    @Resource
    private Cache<String, List<CommentVO>> localCache;
    @Resource
    private LikesService likesService;
    @Resource
    private CommentMapper commentMapper;
    @Resource
    private MqProducer mqProducer;

    @Override
    public CommentVO getCommentById(Long id) {
        String key = COMMENT_KEY + id;
        String commentByRedis = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isBlank(commentByRedis)) {
            Comment comment = getById(id);
            CommentVO commentVO = BeanUtil.copyProperties(comment, CommentVO.class);
            stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(commentVO), COMMENT_TTL, TimeUnit.SECONDS);
            return commentVO;
        }
        return JSONUtil.toBean(commentByRedis, CommentVO.class);
    }

    @Override
    public Long createComment(CreateCommentRequest createCommentRequest) {
        Comment comment = new Comment();
        BeanUtil.copyProperties(createCommentRequest, comment);
        save(comment);
        return comment.getId();
    }

    @Override
    public boolean updateComment(UpdateCommentRequest updateCommentRequest) {
        return update(new LambdaUpdateWrapper<Comment>()
                .eq(Comment::getId, updateCommentRequest.getId())
                .set(Comment::getContext, updateCommentRequest.getContext()));
    }

    @Override
    public Boolean deleteComment(Long id) {
        //删评论的时候将缓存一并删除 由canal监听
        return removeById(id);
    }

    @Override
    public List<CommentVO> showVideoRootComment(Long id, int current, int pageSize) {
        //是热key查缓存
        String hotKey = "hotVideo__" + id;
        String key = COMMENT_ROOT_VIDEO_KEY + id;
        if (JdHotKeyStore.isHotKey(hotKey)) {
            //log.info("key {} 是热key", hotKey);
            List<CommentVO> cache = getFromCache(key, current, pageSize);
            if (CollectionUtil.isNotEmpty(cache)) {
                return cache;
            }
            log.info("空缓存");
            //查询一下是否有key
            Boolean hasKey = stringRedisTemplate.hasKey(key);
            //如果存在这个key直接返回空值，说明值为空，不走数据库
            if (Boolean.TRUE.equals(hasKey)) {
                return cache;
            }
        }
        //log.info("key {}不是热key", hotKey);
        //先把他们的子评论查出来全部放到comments这个列表里面
        List<Comment> comments = getComments(id, current, pageSize);
        //异步存入Redis
        List<CommentVO> commentVOS = handleDBCommentList(comments);
        if (JdHotKeyStore.isHotKey(hotKey)) {
            mqProducer.sendCommentMessage(commentVOS);
        }
        return commentVOS;
    }

    @Override
    public List<CommentVO> showChildCommentById(Long id, int current, int pageSize) {
        //查缓存
        String hotKey = "hotVideo__" + id;
        String key = COMMENT_CHILD_VIDEO_KEY + id;
        if (JdHotKeyStore.isHotKey(hotKey)) {
            //log.info("key {} 是热key", hotKey);
            List<CommentVO> cache = getFromCache(key, current, pageSize);
            if (CollectionUtil.isNotEmpty(cache)) {
                return cache;
            }
            //查询一下是否有key
            Boolean hasKey = stringRedisTemplate.hasKey(key);
            //如果存在这个key直接返回空值，说明值为空，不走数据库
            if (Boolean.TRUE.equals(hasKey)) {
                return cache;
            }
        }
        //log.info("key {}不是热key", hotKey);
        //从数据库查出这个视频的子评论
        List<Comment> childByPage = commentMapper.getChildByPage(id, (current - 1) * pageSize, pageSize);
        List<CommentVO> commentVOS = handleDBCommentList(childByPage);
        if (JdHotKeyStore.isHotKey(hotKey)) {
            mqProducer.sendCommentMessage(commentVOS);
        }
        return commentVOS;
    }

    private CommentVO convertToVO(Comment comment, Map<Long, UserDTO> userIdsMap) {
        CommentVO commentVO = BeanUtil.copyProperties(comment, CommentVO.class);
        commentVO.setAuthor(userIdsMap.get(comment.getPubId()));
        return commentVO;
    }

    private List<CommentVO> getFromCache(String key, int current, int pageSize) {
        //改造为分页查询
        int[] startEnd = PageUtil.transToStartEnd(current - 1, pageSize);
        //本地缓存要控制更细的颗粒度
        List<CommentVO> cache = localCache.get(key + ":" + current, s -> {
            Set<String> strings = stringRedisTemplate.opsForZSet().reverseRange(key, startEnd[0], startEnd[1]);
            //处理空缓存
            if (CollectionUtil.isEmpty(strings) || (strings.size() == 1 && strings.contains("EMPTY_PAGE"))) {
                return Collections.emptyList();
            }
            return strings.stream().map(jsonStr -> JSONUtil.toBean(jsonStr, CommentVO.class)).collect(Collectors.toList());
        });
        return getTimeData(cache);
    }

    private List<Comment> getComments(Long id, int current, int pageSize) {
        //首先先查出这个视频所有的评论(分页查询)
        //方案：将前10条的跟评论查出来，再使用别的查询查出他们的子评论
        Page<Comment> page = page(new Page<>(current, pageSize),
                new QueryWrapper<Comment>().eq("video_id", id).isNull("root_comment_id"));
        return page.getRecords();
    }

    private List<CommentVO> handleDBCommentList(List<Comment> comments) {
        //找出所有出现的用户
        Set<Long> userIds = comments.stream().map(Comment::getPubId).collect(Collectors.toSet());
        if (CollectionUtil.isEmpty(userIds)) {
            return Collections.emptyList();
        }
        //将用户查询出来
        Map<Long, UserDTO> userIdsMap = userClient.getUserByIdBatch(userIds);
        //构建评论树
        Map<Long, CommentVO> commentMap = new HashMap<>();
        // 先处理所有评论基础信息
        comments.forEach(comment -> {
            CommentVO vo = convertToVO(comment, userIdsMap);
            commentMap.put(comment.getId(), vo);
        });
        //设置回复目标用户
        comments.forEach(comment -> {
            CommentVO currentVO = commentMap.get(comment.getId());
            if (comment.getToCommentId() != null) {
                CommentVO targetComment = commentMap.get(comment.getToCommentId());
                if (targetComment != null) {
                    currentVO.setReplyToUser(targetComment.getAuthor());
                }
            }
            currentVO.setAuthor(userIdsMap.get(comment.getPubId()));
            currentVO.setChildCount(2);
        });
        //查询实时数据
        List<CommentVO> commentVOS = getTimeData(new ArrayList<>(commentMap.values()));
        commentVOS.sort(Comparator
                .comparing(CommentVO::getLikes, Comparator.reverseOrder())
                .thenComparing(CommentVO::getCreateTime)
        );
        return commentVOS;
    }

    private List<CommentVO> getTimeData(List<CommentVO> commentVOS) {
        return commentVOS.stream().peek(commentVO -> {
            LikeQueryRequest likeQueryRequest = new LikeQueryRequest(COMMENT_BIZ, commentVO.getId());
            commentVO.setIsLiked(likesService.isUserLiked((String) StpUtil.getLoginId(), COMMENT_BIZ, commentVO.getId()));
            commentVO.setLikes(Math.toIntExact(likesService.queryLikeNum(likeQueryRequest))
            );
        }).collect(Collectors.toList());
    }
}




